package com.gateway.video.rtsp.stream.h264;

import com.gateway.video.rtsp.stream.VideoStream;
import com.gateway.video.rtsp.stream.pull.PullSource;
import com.gateway.video.rtsp.stream.pull.PullVideoConfig;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacpp.avutil;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.FrameRecorder;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import static org.bytedeco.javacpp.avcodec.av_packet_unref;
import static org.bytedeco.javacpp.avformat.avformat_network_init;

@Slf4j
public class H264VideoStream implements VideoStream {
    public static final String FORMAT = "h264";

    private static final Map<String, FFmpegFrameRecorder> RECORDER_MAP = new ConcurrentHashMap();

    @Autowired
    private PullVideoConfig pullVideoConfig;

    @Override
    public boolean isSupport() {
        return true;
    }

    @Override
    public void start(String pushUrl) {
        var grabber = PullSource.getInstance().grabber;
        var recorder = new FFmpegFrameRecorder(pushUrl, 0);
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        recorder.setVideoOption("tune", "zerolatency");
        recorder.setMaxDelay(500);
        recorder.setGopSize(10);
        recorder.setVideoOption("preset", "ultrafast");
        recorder.setFormat(FORMAT);
        avformat_network_init();

        double frameLength = grabber.getLengthInFrames();
        long frameTime = grabber.getLengthInTime();
        double v = frameLength * 1000 * 1000 / frameTime;
        recorder.setFrameRate(v);
        recorder.setVideoBitrate(Optional.ofNullable(pullVideoConfig.getSource().getVideoBitrate()).orElse(3500));
        recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
        recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);

        try {
            recorder.start(grabber.getFormatContext());

            grabber.flush();
            long errCount = 0;
            for(int no_frame_index = 0; no_frame_index < 10 || errCount > 1;) {
                try {
                    var avPacket = grabber.grabPacket();
                    if(avPacket == null || avPacket.size() <= 0 || avPacket.data() == null) {
                        no_frame_index ++;
                        if(log.isWarnEnabled()) {
                            log.warn("Video server miss pack number: {}", no_frame_index);
                        }

                        continue;
                    }
                    errCount += (recorder.recordPacket(avPacket) ? 0 : 1);
                    av_packet_unref(avPacket);
                } catch (IOException e) {
                    if(log.isWarnEnabled()) {
                        log.warn("Video server send packet fail, err_index:{}", errCount);
                    }
                }
            }

            RECORDER_MAP.put(pushUrl, recorder);
        } catch (FrameRecorder.Exception | FrameGrabber.Exception e) {
            if(log.isErrorEnabled()) {
                log.error("Video server start fail", e);
            }
            throw new RuntimeException();
        }
    }

    @Override
    public void pause(String pushUrl) {
        var fFmpegFrameRecorder = RECORDER_MAP.get(pushUrl);
        try {
            fFmpegFrameRecorder.stop();
        } catch (FrameRecorder.Exception e) {
            if(log.isErrorEnabled()) {
                log.error("Video server pause fail", e);
            }
            throw new RuntimeException();
        }
    }

    @Override
    public void close(String pushUrl) {
        var fFmpegFrameRecorder = RECORDER_MAP.get(pushUrl);
        try {
            fFmpegFrameRecorder.close();
        } catch (FrameRecorder.Exception e) {
            if(log.isErrorEnabled()) {
                log.error("Video server close fail", e);
            }
            throw new RuntimeException();
        }
    }
}
